Index: Modules/posixmodule.c =================================================================== --- Modules/posixmodule.c (revision 72811) +++ Modules/posixmodule.c (working copy) @@ -988,6 +988,45 @@ return 0; } +void set_symlink_statA( + const char *path, + WIN32_FILE_ATTRIBUTE_DATA *info, + struct win32_stat *result) +{ + /* Get WIN32_FIND_DATA structure for the path to determine if + it is a symlink */ + WIN32_FIND_DATAA find_data; + HANDLE find_data_handle; + if(!(info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) return; + find_data_handle = FindFirstFileA(path, &find_data); + if(find_data_handle != INVALID_HANDLE_VALUE) + { + if(find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) + result->st_mode |= 0120000; + FindClose(find_data_handle); + } +} + +void set_symlink_statW( + const wchar_t *path, + WIN32_FILE_ATTRIBUTE_DATA *info, + struct win32_stat *result) +{ + /* Get WIN32_FIND_DATA structure for the path to determine if + it is a symlink */ + WIN32_FIND_DATAW find_data; + HANDLE find_data_handle; + if(!(info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) return; + find_data_handle = FindFirstFileW(path, &find_data); + if(find_data_handle != INVALID_HANDLE_VALUE) + { + if(find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) + result->st_mode |= 0120000; + FindClose(find_data_handle); + } +} + + /* Emulate GetFileAttributesEx[AW] on Windows 95 */ static int checked = 0; static BOOL (CALLBACK *gfaxa)(LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID); @@ -1126,6 +1165,9 @@ code = attribute_data_to_stat(&info, result); if (code != 0) return code; + + set_symlink_statA(path, &info, result); + /* Set S_IFEXEC if it is an .exe, .bat, ... */ dot = strrchr(path, '.'); if (dot) { @@ -1164,6 +1206,9 @@ code = attribute_data_to_stat(&info, result); if (code < 0) return code; + + set_symlink_statW(path, &info, result); + /* Set IFEXEC if it is an .exe, .bat, ... */ dot = wcsrchr(path, '.'); if (dot) { @@ -4717,7 +4762,52 @@ } #endif /* HAVE_SYMLINK */ +#if !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) +PyDoc_STRVAR(msc_symlink__doc__, +"symlink(src, dest)\n\n\ +Create a symbolic link pointo to src named dst."); + +static PyObject * +msc_symlink(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"target_is_directory", NULL}; + PyObject *src, *dest; + int target_is_directory = 0; + DWORD res; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i:symlink", + kwlist, &src, &dest, &target_is_directory)) + return NULL; + if (!convert_to_unicode(&src)) { return NULL; } + if (!convert_to_unicode(&dest)) { + Py_DECREF(src); + return NULL; + } + /* TODO: if dest is a directory, ensure target_is_directory==1 + Something like: + target_is_directory ||= ntpath.isdir(dest); + + only ntpath doesn't exist in this context. + */ + Py_BEGIN_ALLOW_THREADS + res = CreateSymbolicLinkW( + PyUnicode_AsUnicode(src), + PyUnicode_AsUnicode(dest), + target_is_directory); + Py_END_ALLOW_THREADS + Py_DECREF(src); + Py_DECREF(dest); + if (!res) + { + return win32_error("symlink", src); + } + + Py_INCREF(Py_None); + return Py_None; +} +#endif /* !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */ + #ifdef HAVE_TIMES #if defined(PYCC_VACPP) && defined(PYOS_OS2) static long @@ -7080,6 +7170,9 @@ #ifdef HAVE_SYMLINK {"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__}, #endif /* HAVE_SYMLINK */ +#if !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) + {"symlink", msc_symlink, METH_VARARGS | METH_KEYWORDS, msc_symlink__doc__}, +#endif /* !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) #ifdef HAVE_SYSTEM {"system", posix_system, METH_VARARGS, posix_system__doc__}, #endif Index: Lib/ntpath.py =================================================================== --- Lib/ntpath.py (revision 72811) +++ Lib/ntpath.py (working copy) @@ -306,13 +306,19 @@ return split(p)[0] # Is a path a symbolic link? -# This will always return false on systems where posix.lstat doesn't exist. +# This will always return false on systems where os.lstat doesn't exist. def islink(path): - """Test for symbolic link. - On WindowsNT/95 and OS/2 always returns false """ - return False + Test whether a path is a symbolic link. + This will always return false for Windows prior to 6.0 + and for OS/2. + """ + try: + st = os.lstat(path) + except (os.error, AttributeError): + return False + return stat.S_ISLNK(st.st_mode) # alias exists to lexists lexists = exists